/*
 * Copyright 2016 Carbs.Wang (AvatarImageView)
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.carbs.harmony.avatarimageview.library;


import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapShader;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.render.Texture;
import ohos.agp.render.Shader;
import ohos.agp.utils.Color;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;
import ohos.global.resource.Resource;
import ohos.global.resource.ResourceManager;
import ohos.media.image.PixelMap;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;

public class AvatarImageView extends Image {

    public static final int[] COLORS = {0xff44bb66,
            0xff55ccdd,
            0xffbb7733,
            0xffff6655,
            0xffffbb44,
            0xff44aaff};

    private static final int COLORS_NUMBER = COLORS.length;
    private static final int DEFAULT_TYPE_BITMAP = 0;
    private static final int DEFAULT_TYPE_TEXT = 1;
    private static final String DEFAULT_TEXT = "";

    private PixelMap mBitmap;
    private int mType = DEFAULT_TYPE_BITMAP;
    private Paint mPaintDraw;
    private PixelMapShader mBitmapShader;//used to adjust position of bitmap
    private Matrix mMatrix;//used to adjust position of bitmap
    private Color mBgColor = new Color(COLORS[0]);

    private float mTextSizeRatio = DEFAULT_TEXT_SIZE_RATIO;//the text size divides (2 * mRadius)
    private float mTextMaskRatio = DEFAULT_TEXT_MASK_RATIO;//the inner-radius text divides outer-radius text
    private int mBoarderWidth = DEFAULT_BOARDER_WIDTH;
    private Color mBoarderColor = DEFAULT_BOARDER_COLOR;
    private Color mTextColor = DEFAULT_TEXT_COLOR;
    private boolean mShowBoarder = DEFAULT_BOARDER_SHOW;


    private int mRadius;//the circle's radius
    private int mCenterX;
    private int mCenterY;
    private Paint mPaintTextForeground;//draw text, in text mode
    private Paint mPaintTextBackground;//draw circle, in text mode
    private Paint mPaintCircle;//draw boarder
    private Paint.FontMetrics mFontMetrics;
    private String mText = DEFAULT_TEXT;

    private static final float DEFAULT_TEXT_SIZE_RATIO = 0.4f;
    private static final float DEFAULT_TEXT_MASK_RATIO = 0.8f;
    private static final int DEFAULT_BOARDER_WIDTH = 4;
    private static final Color DEFAULT_BOARDER_COLOR = new Color(0xffffffff);
    private static final Color DEFAULT_TEXT_COLOR = new Color(0xffffffff);
    private static final boolean DEFAULT_BOARDER_SHOW = false;

    private static final String ATTRIBUTE_TextSizeRatio = "aiv_TextSizeRatio";
    private static final String ATTRIBUTE_TextMaskRatio  ="aiv_TextMaskRatio";
    private static final String ATTRIBUTE_BoarderWidth = "aiv_BoarderWidth";
    private static final String ATTRIBUTE_BoarderColor = "aiv_BoarderColor";
    private static final String ATTRIBUTE_TextColor = "aiv_TextColor";
    private static final String ATTRIBUTE_ShowBoarder = "aiv_ShowBoarder";

    public AvatarImageView(Context context) {
        super(context);
        init();
    }

    public AvatarImageView(Context context, AttrSet attrs) {
        super(context, attrs);
        initAttr(context, attrs);
        init();
    }

    public AvatarImageView(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttr(context, attrs);
        init();
    }

    private void initAttr(Context context,AttrSet attrs) {

        boolean isPresentTextSizeRatio = attrs.getAttr(ATTRIBUTE_TextSizeRatio).isPresent();
        if (isPresentTextSizeRatio) {
            mTextSizeRatio = attrs.getAttr(ATTRIBUTE_TextSizeRatio).get().getFloatValue();
        } else {
            mTextSizeRatio = DEFAULT_TEXT_SIZE_RATIO;
        }
        boolean isPresentTextMaskRatio = attrs.getAttr(ATTRIBUTE_TextMaskRatio).isPresent();
        if (isPresentTextMaskRatio) {
            mTextMaskRatio = attrs.getAttr(ATTRIBUTE_TextMaskRatio).get().getFloatValue();
        } else {
            mTextMaskRatio = DEFAULT_TEXT_MASK_RATIO;
        }
        boolean isPresentBoarderWidth = attrs.getAttr(ATTRIBUTE_BoarderWidth).isPresent();
        if (isPresentBoarderWidth) {
            mBoarderWidth = attrs.getAttr(ATTRIBUTE_BoarderWidth).get().getIntegerValue();
        } else {
            mBoarderWidth = DEFAULT_BOARDER_WIDTH;
        }
        boolean isPresentBoarderColor = attrs.getAttr(ATTRIBUTE_BoarderColor).isPresent();
        if (isPresentBoarderColor) {
            mBoarderColor = attrs.getAttr(ATTRIBUTE_BoarderColor).get().getColorValue();
        } else {
            mBoarderColor = DEFAULT_BOARDER_COLOR;
        }
        boolean isPresentTextColor = attrs.getAttr(ATTRIBUTE_TextColor).isPresent();
        if (isPresentTextColor) {
            mTextColor = attrs.getAttr(ATTRIBUTE_TextColor).get().getColorValue();
        } else {
            mTextColor = DEFAULT_TEXT_COLOR;
        }
        boolean isPresentShowBoarder = attrs.getAttr(ATTRIBUTE_ShowBoarder).isPresent();
        if (isPresentShowBoarder) {
            mShowBoarder = attrs.getAttr(ATTRIBUTE_ShowBoarder).get().getBoolValue();
        } else {
            mShowBoarder = DEFAULT_BOARDER_SHOW;
        }
    }

    private void init() {
        this.addDrawTask(new Component.DrawTask() {
            @Override
            public void onDraw(Component compoent, Canvas canvas) {
                if (mBitmap != null && mType == DEFAULT_TYPE_BITMAP) {
                    toDrawBitmap(canvas);
                } else if (mText != null && mType == DEFAULT_TYPE_TEXT) {
                    toDrawText(canvas);
                }
                if(mShowBoarder){
                    drawBoarder(canvas);
                }
            }
        });

        mMatrix = new Matrix();

        mPaintTextForeground = new Paint();
        mPaintTextForeground.setColor(mTextColor);
        mPaintTextForeground.setAntiAlias(true);
        mPaintTextForeground.setTextAlign(TextAlignment.CENTER);

        mPaintTextBackground = new Paint();
        mPaintTextBackground.setAntiAlias(true);
        mPaintTextBackground.setStyle(Paint.Style.FILL_STYLE);

        mPaintDraw = new Paint();
        mPaintDraw.setAntiAlias(true);
        mPaintDraw.setStyle(Paint.Style.FILL_STYLE);

        mPaintCircle = new Paint();
        mPaintCircle.setAntiAlias(true);
        mPaintCircle.setStyle(Paint.Style.STROKE_STYLE);
        mPaintCircle.setColor(mBoarderColor);
        mPaintCircle.setStrokeWidth(mBoarderWidth);
        postLayout();
    }

    @Override
    public void postLayout() {
        super.postLayout();
        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        int contentWidth = getWidth() - paddingLeft - getPaddingRight();
        int contentHeight = getHeight() - paddingTop - getPaddingBottom();
        mRadius = contentWidth < contentHeight ? contentWidth / 2 : contentHeight / 2;

        mCenterX = paddingLeft + mRadius;
        mCenterY = paddingTop + mRadius;
        refreshTextSizeConfig();
    }

    private void refreshTextSizeConfig() {
        mPaintTextForeground.setTextSize((int)(mTextSizeRatio * 2 * mRadius));
        mFontMetrics = mPaintTextForeground.getFontMetrics();
    }

    private void toDrawBitmap(Canvas canvas) {
        if(mBitmap == null) return;
        drawBitmap(canvas, mBitmap, true);
    }

    private void drawBitmap(Canvas canvas, PixelMap bitmap, boolean adjustScale) {
        refreshBitmapShaderConfig(bitmap, adjustScale);
        mPaintDraw.setShader(mBitmapShader,Paint.ShaderType.PIXELMAP_SHADER);
        canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaintDraw);
    }

    private void toDrawText(Canvas canvas){
        if(mText.length() == 1) {
            //draw text to the view's canvas directly
            drawText(canvas);
        }else{
            //draw text with clip effect, need to create a bitmap
            drawBitmap(canvas, createClipTextBitmap((int) (mRadius / mTextMaskRatio)), false);
        }
    }

    private void refreshBitmapShaderConfig(PixelMap bitmap, boolean adjustScale){
        PixelMapHolder pixelmapholder = new PixelMapHolder(bitmap);
        mBitmapShader = new PixelMapShader(pixelmapholder, Shader.TileMode.CLAMP_TILEMODE, Shader.TileMode.CLAMP_TILEMODE);
        mMatrix.reset();
        int bitmapWidth = bitmap.getImageInfo().size.width;
        int bitmapHeight = bitmap.getImageInfo().size.height;
        if(adjustScale) {
            int bSize = Math.min(bitmapWidth, bitmapHeight);
            float scale = mRadius * 2.0f / bSize;//TODO
            mMatrix.setScale(scale, scale);
            if (bitmapWidth > bitmapHeight) {
                mMatrix.postTranslate(-(bitmapWidth * scale / 2 - mRadius - getPaddingLeft()), getPaddingTop());
            } else {
                mMatrix.postTranslate(getPaddingLeft(), -(bitmapHeight * scale / 2 - mRadius - getPaddingTop()));
            }
        }else{
            mMatrix.postTranslate(-(bitmapWidth * 1 / 2 - mRadius - getPaddingLeft()), -(bitmapHeight * 1 / 2 - mRadius - getPaddingTop()));
        }

        mBitmapShader.setShaderMatrix(mMatrix);
    }

    private void refreshTextConfig() {
        if (mBgColor != mPaintTextBackground.getColor()) {
            mPaintTextBackground.setColor(mBgColor);
        }
        if (mTextColor != mPaintTextForeground.getColor()) {
            mPaintTextForeground.setColor(mTextColor);
        }
    }

    public void setBitmap(PixelMap bitmap) {
        if (bitmap == null) {
            return;
        }
        if (this.mType != DEFAULT_TYPE_BITMAP || bitmap != this.mBitmap) {
            this.mBitmap = bitmap;
            this.mType = DEFAULT_TYPE_BITMAP;
            invalidate();
        }
    }

    public void setDrawable(Element drawable) {
        PixelMap bitmap = getBitmapFromDrawable(drawable);
        setBitmap(bitmap);
    }

    private PixelMap getBitmapFromDrawable(Element drawable) {
        if (drawable == null) {
            return null;
        }
        if (drawable instanceof PixelMapElement) {
            return ((PixelMapElement) drawable).getPixelMap();
        }

        return null;
    }

    public void setImageResource(int resId) {
        Element element = Utils.getPixelMapDrawable(getContext(), resId);
        if (element != null)
            setDrawable(element);
    }

    public void setTextColor(Color textColor) {
        if (this.mTextColor != textColor) {
            mTextColor = textColor;
            mPaintTextForeground.setColor(mTextColor);
            invalidate();
        }
    }

    public void setTextAndColorSeed(String text, String colorSeed) {
        setTextAndColor(text, getColorBySeed(colorSeed));
    }

    public Color getColorBySeed(String seed) {
        if (seed == null || seed.length() == 0) {
            return new Color(COLORS[0]);
        }
        return new Color(COLORS[Math.abs(seed.hashCode() % COLORS_NUMBER)]);
    }

    public void setImageDrawable(Element drawable) {
        setDrawable(drawable);
    }


    private void drawBoarder(Canvas canvas){
        canvas.drawCircle(mCenterX, mCenterY, mRadius - mBoarderWidth /2, mPaintCircle);
    }

    private void drawText(Canvas canvas) {
        refreshTextConfig();
        canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaintTextBackground);
        canvas.drawText(mPaintTextForeground, mText, mCenterX, mCenterY + Math.abs(mFontMetrics.top + mFontMetrics.bottom) / 2);
    }

    public void setTextAndColor(String text, Color bgColor) {
        if (this.mType != DEFAULT_TYPE_TEXT || !stringEqual(text, this.mText) || bgColor != this.mBgColor) {
            this.mText = text;
            this.mBgColor = bgColor;
            this.mType = DEFAULT_TYPE_TEXT;
            postLayout();
            invalidate();
        }
    }

    private PixelMap createClipTextBitmap(int bitmapRadius){
        PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
        initializationOptions.size =  new Size();
        initializationOptions.size.height = bitmapRadius * 2;
        initializationOptions.size.width = bitmapRadius * 2;
        initializationOptions.pixelFormat = PixelFormat.ARGB_8888;
        PixelMap bitmapClipText = PixelMap.create(initializationOptions);
        Canvas canvasClipText = new Canvas(new Texture(bitmapClipText));
        Paint paintClipText = new Paint();
        paintClipText.setStyle(Paint.Style.FILL_STYLE);
        paintClipText.setAntiAlias(true);
        paintClipText.setColor(mBgColor);
        canvasClipText.drawCircle(bitmapRadius, bitmapRadius, bitmapRadius, paintClipText);

        paintClipText.setTextSize((int)(mTextSizeRatio * mRadius * 2));
        paintClipText.setColor(mTextColor);
        paintClipText.setTextAlign(TextAlignment.CENTER);
        Paint.FontMetrics fontMetrics = paintClipText.getFontMetrics();
        canvasClipText.drawText(paintClipText, mText, bitmapRadius,bitmapRadius + Math.abs(fontMetrics.top + fontMetrics.bottom) / 2);
        return bitmapClipText;
    }

    private boolean stringEqual(String a, String b){
        if(a == null){
            return (b == null);
        }else{
            if(b == null){
                return false;
            }
            return a.equals(b);
        }
    }

}
